#ifndef RENDERER_H
#define RENDERER_H

#include "scene.h"

#include <QObject>
#include <QVector>
#include <QFuture>
#include <QFutureWatcher>
#include <QMutex>
#include <QRgb>
#include <QImage>
#include <QtConcurrentRun>
#include <QtConcurrentMap>
#include <QtConcurrentFilter>
#include <QList>
#include <QDataStream>

#include <string.h>

#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif

#ifdef OPEN_CL
# include <QtOpenCL/qclcontext.h>
# include <QtOpenCL/qclprogram.h>
# include <QtOpenCL/qclimage.h>
#endif //OPEN_CL

#ifndef Q_MOC_RUN
#include <boost/random/taus88.hpp>
#include <boost/random.hpp>
#include <boost/format.hpp>
#include <boost/bind.hpp>
#endif


#include <ImfChannelList.h>
#include <ImfHeader.h>
#include <ImfOutputFile.h>
#include <ImfFrameBuffer.h>
using namespace Imf;

using namespace boost::random;


class Renderer: public QThread
{
    Q_OBJECT

    int _width;
    int _height;
    int _samples;
    int _depth;
    int _progress;
    bool _isStarted;
    bool _isPaused;

    Scene* _scene;
#ifdef OPEN_CL
    bool _OCLEnable;
    bool _OCLAvailable;
    QCLContext _context;
    QCLProgram _program;
    QCLKernel _raytracer;
#endif

    QVector<double> _zbuffer;
    QMutex _mut;
    taus88 _rng;
    QFutureWatcher<void>* _watcher;

    void _render();


public:

    uniform_01<taus88&> _ran;
    Vector* _emittersPixelBuffer;
    Vector* _emittersPixelBufferCopy;
    double _probabilities[3];
    explicit Renderer(Scene *scene, int widht, int height, int samples = 128, int depth = 5, QObject *parent=0);
    ~Renderer();
    void render();
    void update();
    void computeImage(QImage& image, double gamma, double luminosity);
    Vector trace(const Ray &ray, int depth);
    void saveAsJpeg(QString filename, double gamma, double luminosity);
    void saveAsEXR(QString filename);

    int width() const;
    int height() const;
    int depth() const;
    int samples() const;
    const Scene* scene() const;
    bool isPaused();
    bool isStarted();
    void setWidth(int value);
    void setHeight(int value);
    void setSamples(int samples);
    void setDepth(int depth);

   friend void _renderline(Renderer* r, int y);

   // void evaluateRaw(float* inputArray, float* outputArray, CHDRData *pData, int arrayWidth, int iRow);

#ifdef OPEN_CL
    bool isOCLAvailable() const;
#endif
    static inline void uvw (const Vector& v1, Vector& v2, Vector& v3);

public slots:
    void paused();
    void resumed();
    void stoped();

signals:
    void sampleComputed(int);

};

QDataStream &operator<<(QDataStream &, const Renderer &);
QDataStream &operator>>(QDataStream &, Renderer &);

#endif // RENDERER_H
